package org.giovanolli.mumi.view.validator;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Locale;

import javax.persistence.Column;

import org.apache.commons.beanutils.PropertyUtils;
import org.giovanolli.mumi.controller.AbstractModel;
import org.giovanolli.mumi.entities.IdentificableEntity;
import org.giovanolli.mumi.entities.filter.EntityFilter;
import org.giovanolli.mumi.service.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
import org.springframework.binding.validation.ValidationContext;
import org.springframework.context.MessageSource;

/**
 * The Class AbstractViewStateModelValidator.
 * 
 * @param <T>
 *            the generic type
 * @param <M>
 *            the generic type
 */
public abstract class AbstractViewStateModelValidator<T extends IdentificableEntity, M extends AbstractModel<T>> {

	@Autowired
	protected MessageSource messageSource;

	private static final String UNIQUE_VALUE_VIOLATION = "admin.message.duplicateKeyException";

	/**
	 * Gets the filter.
	 * 
	 * @return the filter
	 */
	public abstract EntityFilter<T> getFilter();

	/**
	 * Gets the service.
	 * 
	 * @return the service
	 */
	public abstract Service<T> getService();

	/**
	 * Validate unique.
	 * 
	 * @param model
	 *            the model
	 * @param context
	 *            the context
	 * @return true, if successful
	 * @throws NoSuchMethodException
	 *             the no such method exception
	 * @throws IllegalAccessException
	 *             the illegal access exception
	 * @throws InvocationTargetException
	 *             the invocation target exception
	 */
	public boolean validateUnique(M model, ValidationContext context) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
		EntityFilter<T> filter = getFilter();
		filter.setExactMatches(true);
		boolean isuniqueFieldFilterable = false;
		for (Field field : model.getEditedEntity().getClass().getDeclaredFields()) {
			Column annotation = field.getAnnotation(javax.persistence.Column.class);
			if (annotation != null && annotation.unique()) {
				Object value = PropertyUtils.getProperty(model, field.getName());
				if (value != null) {
					isuniqueFieldFilterable = true;
				}
				String filterName = field.getName();
				PropertyUtils.setProperty(filter, filterName, value);
			}
		}
		if (!isuniqueFieldFilterable) {
			return true;
		}
		PropertyUtils.setProperty(filter, "notId", model.getEditedEntity().getId());
		List<T> rv = getService().getList(filter);
		return rv.size() == 0;
	}

	/**
	 * Validate view.
	 * 
	 * @param model
	 *            the model
	 * @param context
	 *            the context
	 * @throws NoSuchMethodException
	 *             the no such method exception
	 * @throws IllegalAccessException
	 *             the illegal access exception
	 * @throws InvocationTargetException
	 *             the invocation target exception
	 */
	public void validateView(M model, ValidationContext context) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
		MessageContext messages = context.getMessageContext();
		if (!validateUnique(model, context)) {
			messages.addMessage(new MessageBuilder().error().defaultText(messageSource.getMessage(UNIQUE_VALUE_VIOLATION, null, new Locale("es"))).build());
		}
		return;
	}

}